/*******************************************************************************
 * Copyright (c) 2014 Francesco Gabbrielli
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Francesco Gabbrielli - initial API and implementation
 ******************************************************************************/
package it.francescogabbrielli.android.solidalapp;

import java.util.ArrayList;
import java.util.List;

import it.francescogabbrielli.android.solidalapp.ChatMessage.Type;
import it.francescogabbrielli.android.solidalapp.SolidalApp.DataKey;
import it.francescogabbrielli.android.solidalapp.deviceendpoint.model.Device;
import it.francescogabbrielli.android.solidalapp.file.RequestFileActivity;
import it.francescogabbrielli.android.solidalapp.file.ResponseFileActivity;
import it.francescogabbrielli.android.solidalapp.home.HomeActivity;
import it.francescogabbrielli.android.solidalapp.response.ResponseActivity;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.Bundle;
import android.os.PowerManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;

import com.google.android.gms.gcm.GoogleCloudMessaging;

public class GCMMessageReceiver extends BroadcastReceiver {
	
	private final static String TAG = SolidalApp.TAG + " GCM Receiver";
	
	public enum Action {
		NULL					(null),
		GCM_REQUEST_RECEIVED  	(ResponseActivity.class), 
		GCM_RESPONSE_RECEIVED	(HomeActivity.class), 
		GCM_FEEDBACK_RECEIVED	(null),
		GCM_REQUEST_CHAT_RECEIVED	(ResponseFileActivity.class), 
		GCM_RESPONSE_CHAT_RECEIVED	(RequestFileActivity.class);
		Class<? extends Activity> activity;
		Action(Class<? extends Activity> activity) {
			this.activity = activity;
		}
		public final static Action valueOf(Intent intent) {
			try {
				return valueOf(intent.getAction());
			} catch(Throwable e) {
				return NULL;
			}
		}
	};
	
	class GCMAction extends it.francescogabbrielli.android.solidalapp.Action {
		Action action;
		public GCMAction(Action action) {
			this.action = action;
		}
		@Override
		public void execute() throws Exception {
		}
		@Override
		public void onPostExecute() throws Exception {
		}
	}
	
    @Override
    public void onReceive(Context context, Intent intent) {
    	
    	SolidalApp app = (SolidalApp) context.getApplicationContext();

    	String regId = intent.getStringExtra("registration_id");
    	if (app.getStatus().getGcmId()==null && regId!=null) {
    		Log.i(TAG, "Register from receiver: "+regId);
			app.getStatus().setGcmId(regId);
			app.registerToServices();
    	}
    	
    	GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);

		Bundle extras = intent.getExtras();
	    String messageType = gcm.getMessageType(intent);
	    
	    Log.d(TAG, "Received message: " + EndpointsUtils.toString(extras));
	    
	    if (!extras.isEmpty()) {  // has effect of unparcelling Bundle
	        /*
	         * Filter messages based on message type. Since it is likely that GCM will be
	         * extended in the future with new message types, just ignore any message types you're
	         * not interested in, or that you don't recognize.
	         */
	        if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
	            Log.d(TAG, "Send error: " + extras.toString());
	        } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
	        	Log.d(TAG, "Deleted messages on server: " + extras.toString());
	        } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
	        		        	
	        	try {
	        		
		        	Activity activity = app.getCurrentActivity();
		        	if (activity!=null)
		        		Log.d(TAG, "Dispatch to activity: "+activity.getClass().getSimpleName());
	        		Device device = (Device) app.getData(DataKey.DEVICE);
	        		
		        	Action action = null;
//		        	long id = Long.valueOf(extras.getString("id"));
		        	String title = "";
		        	String sender = extras.getString("sender");
		        	if (sender==null || "".equals(sender))
		        		sender = context.getString(R.string.anonymous_user);
		        	
		        	String type = extras.getString("mtype");
		        	
		        	// multicast message used for dispatching requests
		        	if ("reg_update".equals(type)) {
		        		
		        		regId = extras.getString("message");
		        		if (regId==null || regId.length()<100)
		        			throw new RuntimeException("Error updating registration: "+regId);
		        		app.getStatus().setGcmId(regId);
		        		if (device!=null)
		        			device.setRegistrationId(app.getStatus().getGcmId());
		        		return;
		        		
		        	} else if (type==null) {
		        		
		        		if (device==null)
		        			return;
		        		
		            	// check current request against the one just sent
		        		SharedPreferences prefs = context.getSharedPreferences(
		        				SolidalApp.class.getCanonicalName(), Context.MODE_PRIVATE);
		        		long deviceId = Long.valueOf(extras.getString("deviceId"));
		            	long currentRegId = device.getId().longValue();
		        		try {
		            		if (currentRegId == deviceId) {
		            			Log.w(TAG, "Receiving the request just sent!");
		            			return;
		            		}
		        		} catch(Exception e) {
		        			Log.e(TAG, "Error checking current request: "+currentRegId, e);
		        		}
		        		
		        		// check distance settings
		        		String receive = prefs.getString(Constants.PREF_NOTIFY, "ALL");
		        		if ("NONE".equals(receive))
		        			return;
		        		else 
		        			try {
			        			int km = Integer.parseInt(receive);
			        			try {
			        				Location loc = (Location) app.getData(DataKey.ADDRESS_LOCATION);
			        				if (loc==null)
			        					return;
			        				double diff = Math.abs(loc.getLatitude()-Double.valueOf(extras.getString("lat")));
			        				if (diff * 122 > km)
			        					return;
			        				diff = Math.abs(loc.getLongitude()-Double.valueOf(extras.getString("lon")));
			        				if (diff * 122 > km)
			        					return;
			        			} catch(Exception e) {
			        				Log.e(TAG, "Error while checking location bounds", e);
			        				return;
			        			}
		        			} catch(NumberFormatException exc) {}
		        		
		        		action = Action.GCM_REQUEST_RECEIVED;
		        		title = context.getString(R.string.request_notification_title, sender);	        	
		        	
		        	// single message used for response
		        	} else if ("accept".equals(type)) {
		        		
		        		action = Action.GCM_RESPONSE_RECEIVED;
		        		title = context.getString(R.string.response_notification_title, sender);
		        		
		        	// single message used for feedback
		        	} else if ("feedback".equals(type)) {
		        		
		        		action = Action.GCM_FEEDBACK_RECEIVED;
		        		title = context.getString(R.string.feedback_notification_title, extras.getString("message"));
		        		
		        	// chat message from request view
		        	} else if (type.contains("response")) {
		        		
		        		action = Action.GCM_RESPONSE_CHAT_RECEIVED;
		        		title = extras.getString("message");
		        		
		        		// add message before it gets lost...
		        		@SuppressWarnings("unchecked")
		        		List<ChatMessage> chat = (List<ChatMessage>) app.getData(DataKey.CHAT_REQUEST);
		        		if (chat==null) {
		        			chat = new ArrayList<ChatMessage>();
		        			app.setData(DataKey.CHAT_REQUEST, chat);
		        		}
		        		chat.add(new ChatMessage(System.currentTimeMillis(), title, Type.INCOMING));
		        		
		        	// chat message from response view
		        	} else if (type.contains("request")) {
		        		
		        		action = Action.GCM_REQUEST_CHAT_RECEIVED;
		        		title = extras.getString("message");
		        		
		        		// add message before it gets lost...
		        		@SuppressWarnings("unchecked")
		        		List<ChatMessage> chat = (List<ChatMessage>) app.getData(DataKey.CHAT_RESPONSE);
		        		if (chat==null) {
		        			chat = new ArrayList<ChatMessage>();
		        			app.setData(DataKey.CHAT_RESPONSE, chat);
		        		}
		        		//chat.add(new ChatMessage(Long.valueOf(extras.getString("id")), title, Type.INCOMING));
		        		chat.add(new ChatMessage(System.currentTimeMillis(), title, Type.INCOMING));
		        		
		        	// other
		        	} else {
		        		
		        		app.alert("Received message type "+type+" unexpected");
		        				        	
		        	}
		        	
		        	Log.d(TAG, "Message processed to "+action);

	        		boolean screenLocked = acquireScreenLock(context);

		        	/*
		        	 * re-intent current activity if null or selected activity
		        	 * matches
		        	 */
		        	if (activity!=null && !screenLocked && (action.activity==null || action.activity==activity.getClass())) {
		        		
		        		Log.v(TAG, "Fire new intent to "+activity.getClass());
		        		new GCMAction(action)
		        			.init(context, intent)
		        			.setAction(action.toString())
		        			.refireActivity(activity.getClass());
		        		
		        	/* 
		        	 * if in background or in a page different from the target one,
		        	 * send a notification that starts a new target activity:
		        	 */
		        	} else {//if (activity==null || activity.getClass()!=action.activity) {
		        		
		        		Log.v(TAG, "Fire notification: "+title);
		        		notify(context, action, extras, title);
		        		
		        	}
		        	
	        	} catch(Exception e) {
	        		Log.e(TAG, "Error receiving GCM message", e);
	        	}
	        }
	    }
	    
	}
    
    private void notify(Context context, 
    			Action action, Bundle extras, String titleString) {
    	
		NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
			.setSmallIcon(R.drawable.ic_notification)
			.setContentTitle(titleString);
			
		String msg = extras.getString("message");
		if (!titleString.equals(msg) && !"OK".equals(msg) && !"KO".equals(msg))
			builder
				.setContentText(msg)
				.setTicker(titleString +":\n" + msg);
		
		SharedPreferences prefs = context.getSharedPreferences(SolidalApp.class.getCanonicalName(), Context.MODE_PRIVATE);
		int notificationId = 0;
		synchronized (prefs) {
			int last = prefs.getInt(Constants.LAST_NOTIFICATION, 0) + 1;
			prefs.edit().putInt(Constants.LAST_NOTIFICATION, last).commit();
			notificationId = last+1;
		}
		
		Intent resultIntent = new Intent(context, action.activity);
		resultIntent.setAction(action.toString());
		resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
		extras.putInt(Constants.NOTIFICATION, notificationId);
		resultIntent.putExtras(extras);
		
		TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
		stackBuilder.addParentStack(action.activity);
		stackBuilder.addNextIntent(resultIntent);
		PendingIntent resultPendingIntent =
		        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);	        		

		builder.setContentIntent(resultPendingIntent);
		NotificationManager manager = 
		        (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
		Notification note = builder.setDefaults(Notification.DEFAULT_SOUND).build();
		manager.notify(notificationId, note);
		Log.v(TAG, "Notified "+notificationId);
	}
    
    private boolean acquireScreenLock(Context context) {
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        if (pm!=null && !pm.isScreenOn()) {
	        PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, SolidalApp.TAG);
	        wakeLock.acquire(10*1000);
	        return true;
//	        PowerManager.WakeLock wakeLock2 = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, SolidalApp.TAG);
//	        wakeLock2.acquire(10*1000);
        }
        return false;
    }
    
}
